/*
This file is part of Docking@Home.
http://docking.gcl.cis.udel.edu
Copyright (C) 2009 University of Delaware

Docking@Home is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Docking@Home is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Docking@Home.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "Interact.h"

NewsTicker::NewsTicker(string hostName,string filePath,int port){
	//empty?
	if(!hostName.length())
		hostName="docking.cis.udel.edu";
	//empty?
	if(!filePath.length())
		filePath="/ticker.php";
	//save the host and file
	this->host=hostName;
	this->file=filePath;
	//no internet
	this->news="Unable to connect to news host.";
	this->news.append(TICKER_STR_LEN-this->news.length()-1,' ');
	//make some space for news
	this->ticker=new char[TICKER_STR_LEN];
	memset(this->ticker,'\0',TICKER_STR_LEN);
	//set to force an update
	this->updated=this->ticked=this->modified=0;
	//setup and check the connection
#ifdef WIN32
	WSAData wsaData;
	int code = WSAStartup(MAKEWORD(1, 1), &wsaData);
	if(code != 0){
		printf("%d\n",code);
		return;
	}
#endif
	//host="123.213.43.23";
	this->connection=new happyhttp::Connection(this->host.c_str(), port);
	this->connection->setcallbacks(&this->OnBegin, &this->OnData, &this->OnComplete, (void*)this);
	//if we can connect to the server
	try{
		this->connection->connect();
		this->connected=true;
	}
	catch(exception &e){
		printf("%s",e.what());
		this->connected=false;
	}
	//try to get the news
	this->Tick(this->connected);
}
NewsTicker::~NewsTicker(){
	//delete allocated mem
	if(this->ticker)
		delete []this->ticker;
	if(this->connection)
		delete this->connection;
#ifdef WIN32
	WSACleanup();
#endif
}
void NewsTicker::Tick(bool force){
	//get the time
	timeb current;
	ftime(&current);
	current.time=current.time*1000+current.millitm;
	//do we need to tick?
	if(current.time-this->ticked>TICK_FREQ_MS){
		//update the tick
		this->ticked=current.time;
		//move the ticker
		this->news.append(1,this->news[0]);
		this->news.erase(this->news.begin());
		//update the display
		strcpy(this->ticker,this->news.substr(0,TICKER_STR_LEN).c_str());
	}
	//check the elapsed time or no connection
	if(!force&&current.time-this->updated<UPDATE_FREQ_MS){
		return;				
	}
	//ask for the page
	try{
		connection->request("GET", this->file.c_str(), 0,0,0);
	}
	catch(exception &e){
		printf("%s",e.what());	
		this->connected=false;
		return;
	}
	//update the time stamp
	this->updated=current.time;
	//check if we got some data
	while(connection->outstanding()){
		try{connection->pump();}
		catch(exception &e){printf("%s",e.what());	break;}
		//check the time
		ftime(&current);
		current.time=current.time*1000+current.millitm;
		//if we have to give up
		if(current.time-this->updated>DOWNLOAD_TIMEOUT_MS)
			break;
	}
}
char *NewsTicker::getNews(){return this->ticker;}

void NewsTicker::OnBegin(const happyhttp::Response* r, void *object){
	//reset the string
	((NewsTicker*)object)->page="";
}
void NewsTicker::OnData(const happyhttp::Response* r, void *object,const unsigned char* data, int n){
	//copy the data to the end of the string
	for(int i=0;i<n;i++){
		((NewsTicker*)object)->page.push_back(data[i]);
	}
}
void NewsTicker::OnComplete(const happyhttp::Response* r, void *object){
	//get the time
	unsigned long mod=strtoul(NewsTicker::between(((NewsTicker*)object)->page,"<time>","</time>").c_str(),NULL,10);
	if(((NewsTicker*)object)->modified!=mod){
		//save the modified time
		((NewsTicker*)object)->modified=mod;
		//get the news
		((NewsTicker*)object)->news=NewsTicker::between(((NewsTicker*)object)->page,"<news>","</news>");
		//if the tickers won't be full
		if(((NewsTicker*)object)->news.length()<TICKER_STR_LEN-1)
			((NewsTicker*)object)->news.append(TICKER_STR_LEN-((NewsTicker*)object)->news.length()-1,' ');
	}
}
string NewsTicker::between(string source,string start,string end){
	source.insert(source.begin(),' ');
	int front=source.find(start);
	if(front==0)
		return "";
	int back=source.find(end);
	if(back==0)
		return "";
	front+=start.length();
	back=back-front;
	if(back<1)
		return "";
	return source.substr(front,back);
}
